package com.bcx.wind.workflow.jdbc;

import com.bcx.wind.workflow.access.AbstractAccess;
import com.bcx.wind.workflow.access.DbInit;
import com.bcx.wind.workflow.access.WindResultObjectHandler;
import com.bcx.wind.workflow.access.WindResultSetHandler;
import com.bcx.wind.workflow.access.paging.PagingBuilder;
import com.bcx.wind.workflow.db.tran.WindTransactionManager;
import com.bcx.wind.workflow.db.util.WindDbRunner;
import com.bcx.wind.workflow.exception.WorkflowException;
import com.bcx.wind.workflow.support.Assert;
import com.bcx.wind.workflow.support.ObjectHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 自定义持久层处理器
 *
 * @author zhanglei
 */
public class JdbcAccess extends AbstractAccess {

    protected static final Logger LOGGER = LoggerFactory.getLogger(JdbcAccess.class);

    /**
     * 数据源
     */
    protected DataSource dataSource;

    /**
     * 工作流事务管理器
     */
    protected WindTransactionManager windTransactionManager;

    /**
     *  持久层工具
     */
    protected final WindDbRunner runner = new WindDbRunner();


    public JdbcAccess(){
        throw new WorkflowException("sorry! wind workflow engine not support no params construct!");
    }

    public JdbcAccess(DataSource dataSource){
        Assert.nonEmpty(dataSource);
        this.dataSource = dataSource;
        this.windTransactionManager = WindTransactionManager.getInstance();
        this.paging = PagingBuilder.buildPaging(getConnection());
        //初始化数据库
        initDB(getConnection());
    }

    protected void initDB(Connection connection){
        String initSql = DbInit.getDbSql(connection);
        try {
            runner.saveOrUpdate(connection,initSql);
        } catch (SQLException e) {
            e.printStackTrace();
           //此处拒绝抛出异常
        }
        this.windTransactionManager.commit();
    }

    @Override
    protected int saveOrUpdate(String sql, Object[] args) {
        Connection connection = getConnection();
        if(LOGGER.isDebugEnabled()){
            LOGGER.debug("get connection: "+connection+" ,run sql: "+sql+" params: "+ Arrays.asList(args));
        }
        try {
            int ret =  runner.saveOrUpdate(connection,sql,args);
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(" run success! result: "+ret);
            }
            return ret;
        } catch (SQLException e) {
            e.printStackTrace();
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(e.getMessage());
            }
            windTransactionManager.rollback();

            throw new WorkflowException(e.getMessage());
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    protected <T> List<T> query(String sql, Object[] args, Class<T> clazz) {
        Connection connection = getConnection();

        if(LOGGER.isDebugEnabled()){
            LOGGER.debug("get connection: "+connection+" ,run sql: "+sql+" params: "+ Arrays.asList(args));
        }
        try {
            List<T> result = this.runner.query(connection,sql,new WindResultSetHandler<>(clazz),args);
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(" run success! query total: "+result.size());
            }
            return result;
        } catch (SQLException e) {
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(e.getMessage());
            }
            e.printStackTrace();
            windTransactionManager.rollback();
            throw new WorkflowException(e.getMessage());
        }
    }


    @Override
    protected <T> T queryOne(String sql, Object[] args, Class<T> clazz) {
        List<T> ret = query(sql,args,clazz);

        if(!ObjectHelper.isEmpty(ret)){
            if(ret.size() > 1){
                if(LOGGER.isDebugEnabled()){
                    LOGGER.debug("queryOne operate back result total is more! ");
                }
                throw new WorkflowException("queryOne operate back result total is more!");
            }
            return ret.get(0);
        }
        return null;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected Object queryObject(String sql, Object[] args) {
        Connection connection = getConnection();
        if(LOGGER.isDebugEnabled()){
            LOGGER.debug("get connection: "+connection+" ,run sql: "+sql+" params: "+ Arrays.asList(args));
        }
        try {
            Object ret = this.runner.query(connection,sql,new WindResultObjectHandler(),args);
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(" run success! query object: "+ret);
            }
            return ret;
        } catch (SQLException e) {
            e.printStackTrace();
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(e.getMessage());
            }
            windTransactionManager.rollback();
            throw new WorkflowException(e.getMessage());
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public long count(String sql, Object[] args) {
        Connection connection = getConnection();
        if(LOGGER.isDebugEnabled()){
            LOGGER.debug("get connection: "+connection+" ,run sql: "+sql+" params: "+ Arrays.asList(args));
        }

        try {
            Object ret = this.runner.query(connection,sql,new WindResultObjectHandler(),args);
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(" run success! back result: "+ret);
            }
            return (long) ret;
        } catch (SQLException e) {
            e.printStackTrace();
            if(LOGGER.isDebugEnabled()){
                LOGGER.debug(e.getMessage());
            }
            windTransactionManager.rollback();
            throw new WorkflowException(e.getMessage());
        }

    }


    @Override
    public synchronized Connection getConnection() {
        Connection connection = windTransactionManager.getConnection();

        if(connection == null){
            try {
                connection = dataSource.getConnection();
                windTransactionManager.setConnection(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }
}
